//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Diagnostics.Contracts;

namespace OpenLS.Spreadsheet.AnalysisToolPak
{
    internal abstract class DateBasis
    {
        public static DateBasis GetBasis(int t)
        {
            return GetBasis((DateBasisType) t);
        }
        protected abstract DateBasisType Type { get; }

        public static DateBasis GetBasis(DateBasisType basis)
        {
            Contract.Ensures(Contract.Result<DateBasis>() != null);
            Contract.Ensures(Contract.Result<DateBasis>().Type == basis);
            switch (basis)
            {
                case DateBasisType.Actual360:
                    return new Actual360Basis();
                case DateBasisType.Actual365:
                    return new Actual365Basis();
                case DateBasisType.ActualActual:
                    return new ActualActualBasis();
                case DateBasisType.European360:
                    return new European360Basis();
                case DateBasisType.Nasd30360:
                    return new Nasd30360Basis();
                default:
                    throw new NotSupportedException();
            }
        }

        public abstract int DaysInYear(DateTime? dt);

        internal static bool IsValidBasis(int basis)
        {
            return basis >= 0 || basis <= 4;
        }

        protected abstract int DayDifferenceImp(DateTime d1, DateTime d2);

        internal int DayDifference(DateTime d1, DateTime d2)
        {
            if (d1 < d2)
                return -DayDifferenceImp(d2, d1);
            return DayDifferenceImp(d1, d2);
        }

        internal abstract double CoupDays(DateTime settlement, DateTime maturity, Frequency frequency);

        internal abstract double YearDifference(DateTime d1, DateTime d2);




        public static double YearFracOld(DateTime start, DateTime end, DateBasisType basis)
        {
            DateBasis b = GetBasis(basis);
            return b.YearFracImp(start, end);
        }
        public double YearFrac(DateTime start, DateTime end)
        {
            if (start > end)
                return YearFracImp(end, start);
            return YearFracImp(start, end);
        }
        protected  double YearFracImp(DateTime start, DateTime end)
        {
            int days = DaysBetween(start, end);
            double peryear = PerYear(start, end);
            return days / peryear;

        }

        protected virtual double PerYear (DateTime start, DateTime end)
        {
                return DaysInYear(null);
        }


        public virtual int DaysBetween(DateTime start, DateTime end)
        {
            if (start.CompareTo(end) == 1)
            {
                return - DaysBetween(end, start);
            }
            return DaysBetweenImp(start, end);
        }

        internal abstract int DaysBetweenImp(DateTime start, DateTime end);
    }
}